/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPointSetToLabelHierarchy.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkPointSetToLabelHierarchy.h"

#include "vtkDataObjectTypes.h"
#include "vtkDataSetAttributes.h"
#include "vtkDoubleArray.h"
#include "vtkGraph.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkIntArray.h"
#include "vtkLabelHierarchy.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPointSet.h"
#include "vtkSmartPointer.h"
#include "vtkStringArray.h"
#include "vtkTextProperty.h"
#include "vtkTimerLog.h"
#include "vtkUnicodeString.h"
#include "vtkUnicodeStringArray.h"

#include <vector>

vtkStandardNewMacro(vtkPointSetToLabelHierarchy);
vtkCxxSetObjectMacro(vtkPointSetToLabelHierarchy, TextProperty, vtkTextProperty);

vtkPointSetToLabelHierarchy::vtkPointSetToLabelHierarchy()
{
  this->MaximumDepth = 5;
  this->TargetLabelCount = 32;
  this->UseUnicodeStrings = false;
  this->TextProperty = vtkTextProperty::New();
  this->SetInputArrayToProcess(0, 0, 0, vtkDataObject::POINT, "Priority");
  this->SetInputArrayToProcess(1, 0, 0, vtkDataObject::POINT, "LabelSize");
  this->SetInputArrayToProcess(2, 0, 0, vtkDataObject::POINT, "LabelText");
  this->SetInputArrayToProcess(3, 0, 0, vtkDataObject::POINT, "IconIndex");
  this->SetInputArrayToProcess(4, 0, 0, vtkDataObject::POINT, "Orientation");
  this->SetInputArrayToProcess(5, 0, 0, vtkDataObject::POINT, "BoundedSize");
}

vtkPointSetToLabelHierarchy::~vtkPointSetToLabelHierarchy()
{
  if (this->TextProperty)
  {
    this->TextProperty->Delete();
  }
}

void vtkPointSetToLabelHierarchy::SetPriorityArrayName(const char* name)
{
  this->SetInputArrayToProcess(0, 0, 0, vtkDataObject::POINT, name);
}

const char* vtkPointSetToLabelHierarchy::GetPriorityArrayName()
{
  vtkInformation* info =
    this->GetInformation()->Get(vtkAlgorithm::INPUT_ARRAYS_TO_PROCESS())->GetInformationObject(0);
  return info->Get(vtkDataObject::FIELD_NAME());
}

void vtkPointSetToLabelHierarchy::SetSizeArrayName(const char* name)
{
  this->SetInputArrayToProcess(1, 0, 0, vtkDataObject::POINT, name);
}

const char* vtkPointSetToLabelHierarchy::GetSizeArrayName()
{
  vtkInformation* info =
    this->GetInformation()->Get(vtkAlgorithm::INPUT_ARRAYS_TO_PROCESS())->GetInformationObject(1);
  return info->Get(vtkDataObject::FIELD_NAME());
}

void vtkPointSetToLabelHierarchy::SetLabelArrayName(const char* name)
{
  this->SetInputArrayToProcess(2, 0, 0, vtkDataObject::POINT, name);
}

const char* vtkPointSetToLabelHierarchy::GetLabelArrayName()
{
  vtkInformation* info =
    this->GetInformation()->Get(vtkAlgorithm::INPUT_ARRAYS_TO_PROCESS())->GetInformationObject(2);
  return info->Get(vtkDataObject::FIELD_NAME());
}

void vtkPointSetToLabelHierarchy::SetIconIndexArrayName(const char* name)
{
  this->SetInputArrayToProcess(3, 0, 0, vtkDataObject::POINT, name);
}

const char* vtkPointSetToLabelHierarchy::GetIconIndexArrayName()
{
  vtkInformation* info =
    this->GetInformation()->Get(vtkAlgorithm::INPUT_ARRAYS_TO_PROCESS())->GetInformationObject(3);
  return info->Get(vtkDataObject::FIELD_NAME());
}

void vtkPointSetToLabelHierarchy::SetOrientationArrayName(const char* name)
{
  this->SetInputArrayToProcess(4, 0, 0, vtkDataObject::POINT, name);
}

const char* vtkPointSetToLabelHierarchy::GetOrientationArrayName()
{
  vtkInformation* info =
    this->GetInformation()->Get(vtkAlgorithm::INPUT_ARRAYS_TO_PROCESS())->GetInformationObject(4);
  return info->Get(vtkDataObject::FIELD_NAME());
}

void vtkPointSetToLabelHierarchy::SetBoundedSizeArrayName(const char* name)
{
  this->SetInputArrayToProcess(5, 0, 0, vtkDataObject::POINT, name);
}

const char* vtkPointSetToLabelHierarchy::GetBoundedSizeArrayName()
{
  vtkInformation* info =
    this->GetInformation()->Get(vtkAlgorithm::INPUT_ARRAYS_TO_PROCESS())->GetInformationObject(5);
  return info->Get(vtkDataObject::FIELD_NAME());
}

int vtkPointSetToLabelHierarchy::FillInputPortInformation(int port, vtkInformation* info)
{
  if (port == 0)
  {
    info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE());
    info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet");
    info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkGraph");
  }
  return 1;
}

int vtkPointSetToLabelHierarchy::RequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{

  vtkSmartPointer<vtkTimerLog> timer = vtkSmartPointer<vtkTimerLog>::New();
  timer->StartTimer();

  vtkIdType numPoints = 0;
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkDataObject* inData = inInfo->Get(vtkDataObject::DATA_OBJECT());

  vtkGraph* graph = vtkGraph::SafeDownCast(inData);
  if (graph)
  {
    numPoints = graph->GetNumberOfVertices();
  }

  vtkPointSet* ptset = vtkPointSet::SafeDownCast(inData);
  if (ptset)
  {
    numPoints = ptset->GetNumberOfPoints();
  }

  int maxDepth = this->MaximumDepth;
  // maxDepth = (int)ceil(log(1.0 + 7.0*totalPoints/this->TargetLabelCount) / log(8.0));

  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  vtkLabelHierarchy* ouData =
    vtkLabelHierarchy::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));

  if (!ouData)
  {
    vtkErrorMacro("No output data");
    return 0;
  }

  ouData->SetTargetLabelCount(this->TargetLabelCount);
  ouData->SetMaximumDepth(maxDepth);

  if (!inData)
  {
    vtkErrorMacro("Null input data");
    return 0;
  }

  vtkPoints* pts = nullptr;
  vtkDataSetAttributes* pdata = nullptr;

  if (graph)
  {
    pts = graph->GetPoints();
    pdata = graph->GetVertexData();
  }

  if (ptset)
  {
    pts = ptset->GetPoints();
    pdata = ptset->GetPointData();
  }

  vtkDataArray* priorities =
    vtkArrayDownCast<vtkDataArray>(this->GetInputAbstractArrayToProcess(0, inputVector));
  vtkDataArray* sizes =
    vtkArrayDownCast<vtkDataArray>(this->GetInputAbstractArrayToProcess(1, inputVector));
  vtkAbstractArray* labels = this->GetInputAbstractArrayToProcess(2, inputVector);
  vtkIntArray* iconIndices =
    vtkArrayDownCast<vtkIntArray>(this->GetInputAbstractArrayToProcess(3, inputVector));
  vtkDataArray* orientations =
    vtkArrayDownCast<vtkDataArray>(this->GetInputAbstractArrayToProcess(4, inputVector));
  vtkDataArray* boundedSizes =
    vtkArrayDownCast<vtkDataArray>(this->GetInputAbstractArrayToProcess(5, inputVector));

  if (!ouData->GetPoints())
  {
    vtkPoints* oupts = vtkPoints::New();
    ouData->SetPoints(oupts);
    oupts->FastDelete();
  }
  if (pts)
  {
    ouData->GetPoints()->ShallowCopy(pts);
  }
  ouData->GetPointData()->ShallowCopy(pdata);
  vtkSmartPointer<vtkIntArray> type = vtkSmartPointer<vtkIntArray>::New();
  type->SetName("Type");
  type->SetNumberOfTuples(numPoints);
  type->FillComponent(0, 0);
  ouData->GetPointData()->AddArray(type);
  ouData->SetPriorities(priorities);
  if (labels)
  {
    if ((this->UseUnicodeStrings && vtkArrayDownCast<vtkUnicodeStringArray>(labels)) ||
      (!this->UseUnicodeStrings && vtkArrayDownCast<vtkStringArray>(labels)))
    {
      ouData->SetLabels(labels);
    }
    else if (this->UseUnicodeStrings)
    {
      vtkSmartPointer<vtkUnicodeStringArray> arr = vtkSmartPointer<vtkUnicodeStringArray>::New();
      vtkIdType numComps = labels->GetNumberOfComponents();
      vtkIdType numTuples = labels->GetNumberOfTuples();
      arr->SetNumberOfComponents(numComps);
      arr->SetNumberOfTuples(numTuples);
      for (vtkIdType i = 0; i < numTuples; ++i)
      {
        for (vtkIdType j = 0; j < numComps; ++j)
        {
          vtkIdType ind = i * numComps + j;
          arr->SetValue(ind, labels->GetVariantValue(ind).ToUnicodeString());
        }
      }
      arr->SetName(labels->GetName());
      ouData->GetPointData()->AddArray(arr);
      ouData->SetLabels(arr);
    }
    else
    {
      vtkSmartPointer<vtkStringArray> arr = vtkSmartPointer<vtkStringArray>::New();
      vtkIdType numComps = labels->GetNumberOfComponents();
      vtkIdType numTuples = labels->GetNumberOfTuples();
      arr->SetNumberOfComponents(numComps);
      arr->SetNumberOfTuples(numTuples);
      for (vtkIdType i = 0; i < numTuples; ++i)
      {
        for (vtkIdType j = 0; j < numComps; ++j)
        {
          vtkIdType ind = i * numComps + j;
          arr->SetValue(ind, labels->GetVariantValue(ind).ToString());
        }
      }
      arr->SetName(labels->GetName());
      ouData->GetPointData()->AddArray(arr);
      ouData->SetLabels(arr);
    }
  }
  ouData->SetIconIndices(iconIndices);
  ouData->SetOrientations(orientations);
  ouData->SetSizes(sizes);
  ouData->SetBoundedSizes(boundedSizes);
  ouData->SetTextProperty(this->TextProperty);
  ouData->ComputeHierarchy();

  timer->StopTimer();
  vtkDebugMacro("StartupTime: " << timer->GetElapsedTime() << endl);

  return 1;
}

void vtkPointSetToLabelHierarchy::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "MaximumDepth: " << this->MaximumDepth << "\n";
  os << indent << "TargetLabelCount: " << this->TargetLabelCount << "\n";
  os << indent << "UseUnicodeStrings: " << this->UseUnicodeStrings << "\n";
  os << indent << "TextProperty: " << this->TextProperty << "\n";
  this->Superclass::PrintSelf(os, indent);
}
